package com.baizhi.utils;

import com.baizhi.dto.RedisData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

@Component
public class CacheClient2 {
    @Autowired
    private RedisTemplate redisTemplate;
    //创建线程池对象
    private ExecutorService threadPool = new ThreadPoolExecutor(10,10,10,TimeUnit.SECONDS,new ArrayBlockingQueue<>(50));

    /**
     * 利用互斥锁到缓存li面查询数据 避免缓存击穿 如过缓存中没有要到数据库中查询 并放到缓存中
     *  逻辑时间过期 方法
     * @param redisPrefix redis查询数据key的前缀
     * @param id 查询条件
     * @param redisLockPrefix 锁标记前缀
     * @param ttl 数据缓存的时间
     * @param function
     * @return要缓存的对象
     * @param <R>返回值类型
     * @param <T>查询条件的类型
     */
    public <R,T> R queryWithMutex ( String redisPrefix, T id, String redisLockPrefix,Long ttl,Function<T,R> function){
        //1.从redis里面获取商户信息 RedisDate
        String key = redisPrefix+id;
        RedisData redisData = (RedisData)redisTemplate.opsForValue().get(key);
        //2.判断是否过期
        boolean after = LocalDateTime.now().isAfter(redisData.getExpireTime());
        if (after){
            //3.如果过期新开一个线程到数据库里面获取数据
            Boolean success = redisTemplate.opsForValue().setIfAbsent(redisLockPrefix + id, 1,ttl,TimeUnit.SECONDS);
            if (success){
                threadPool.submit(()->{
                    try {
                        //到mysql中去取数据
                        R newObj = function.apply(id);
                        //把shop包装成redisDate类型//ToDo 为了测试先写为三十秒 后续改为30分钟
                        RedisData redisData1 = new RedisData( );
                        redisData1.setDate(newObj);
                        redisData1.setExpireTime(LocalDateTime.now().plusSeconds(ttl));
                        //把redisdate1放到redis中
                        redisTemplate.opsForValue().set(key, redisData1);
                    }finally {
                        redisTemplate.delete(redisLockPrefix + id);
                    }
                });
            }
        }
        //4.返回data数据
        return (R)redisData.getDate();
    }
}
